2.3. Creating the Calculator
We're now going to define our app's view controller, which does the actual work of being a calculator.
Open the ClacHsilopViewController.h file, and add the instance variables shown in the following listing. We also declare it to be a delegate of the InputView class.
// ClacHsilopViewController.h
#import <UIKit/UIKit.h>
#import "InputView.h"
@interface ClacHsilopViewController : UIViewController <InputViewDelegate> {
IBOutlet InputView *inputView;
IBOutlet UITableView *stackTableView;
NSNumberFormatter *decimalFormatter;
NSMutableArray *stack;
}
@end
Before implementing that class, let's set up the GUI. Open ClacHsilopViewController.xib
in Interface Builder. Select the main view and set its background color
to a darker gray, just to make our other components stand out. Then use
the Library to find an InputView, and
drag it to the top of the view. Our table will eventually display the
stack in a right-justified column, so right-justify the InputView as well, using the attribute inspector.
Next, drag out a UITableView, making it fill most of the view, as shown in Figure 6. The table doesn't need to extend to the bottom of the view, since the customized inputView will be appearing on top of it.
Connect the table view's dataSource and delegate outlets to the File's Owner proxy icon, as well as the InputView's ivDelegate outlet. Then connect the inputViewstackTableView outlets to the appropriate objects. Save your work now, and switch back to Xcode. and
Open ClacHsilopViewController.m.
This class will have two primary functions: presenting the contents of
the stack in a table view and handling the actual calculator
functionality in response to the user working the controls.
// ClacHsilopViewController.m
#import "ClacHsilopViewController.h"
@implementation ClacHsilopViewController
- (void)viewDidLoad {
[super viewDidLoad];
stack = [[NSMutableArray alloc] init];
decimalFormatter = [[NSNumberFormatter alloc] init];
decimalFormatter.numberStyle = NSNumberFormatterDecimalStyle;
[stackTableView reloadData];
[inputView becomeFirstResponder];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)o {
return YES;
}
- (void)dealloc {
[stack release];
[decimalFormatter release];
[super dealloc];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)s {
return [stack count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:
UITableViewCellStyleValue1
reuseIdentifier:CellIdentifier] autorelease];
}
cell.detailTextLabel.text = [decimalFormatter stringFromNumber:[stack
objectAtIndex:indexPath.row]];
return cell;
}
- (void)handleError {
// in case of an error, push the current number
// onto the stack instead of just tossing it
NSDecimalNumber *inputNumber = [NSDecimalNumber
decimalNumberWithString:inputView.text];
[stack insertObject:inputNumber atIndex:0];
inputView.text = @"Error";
}
- (void)doEnter {
NSDecimalNumber *inputNumber = [NSDecimalNumber
decimalNumberWithString:inputView.text];
[stack insertObject:inputNumber atIndex:0];
[stackTableView reloadData];
inputView.text = @"0";
}
- (void)doDecimalArithmetic:(SEL)method {
if ([stack count] > 0) {
NSDecimalNumber *inputNumber = [NSDecimalNumber
decimalNumberWithString:inputView.text];
NSDecimalNumber *stackNumber = [stack objectAtIndex:0];
NSDecimalNumber *result = [stackNumber performSelector:method
withObject:inputNumber];
inputView.text = [decimalFormatter stringFromNumber:result];
[stack removeObjectAtIndex:0];
} else {
[self handleError];
}
[stackTableView reloadData];
}
- (void)doTaggedAction:(ActionTag)tag forInputView:(InputView *)iv {
switch (tag) {
case ActionEnter:
[self doEnter];
break;
case ActionDivide:
[self doDecimalArithmetic:@selector(decimalNumberByDividingBy:)];
break;
case ActionMultiply:
[self doDecimalArithmetic:
@selector(decimalNumberByMultiplyingBy:)];
break;
case ActionSubtract:
[self doDecimalArithmetic:@selector(decimalNumberBySubtracting:)];
break;
case ActionAdd:
[self doDecimalArithmetic:@selector(decimalNumberByAdding:)];
break;
default:
break;
}
}
@end
That's it! With this code
in place, you should now be able to build and run the app, see the GUI
appear, and immediately have the RPN input keypad at your disposal.